﻿using BCCommon;
using BCCommon.ExceptionExtensions;
using BCDto.Common.ErrorMessage;
using BCDto.Common.SystemExceptionRecord;
using BCService;
using BCService.Common.ExceptionService;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BCTMSRESTful.Configs
{
    /// <summary>
    /// 用于处理异常的类型。
    /// </summary>
    public class HandlerErrorAttribute : ExceptionFilterAttribute, IActionFilter
    {
        private const string webHostName = "testapi.xablackcoffee.com";
        private IDictionary<string, object> actionArguments;
        private string applicationNamespace;

        private readonly IConfiguration configuration;
        private readonly IApplicationContextService applicationContextService;
        private readonly ISystemExceptionRecordService exceptionRecordService;
        private readonly IDatabaseContext databaseContext;
        private readonly IRedisService redisService;
        public HandlerErrorAttribute(IConfiguration configuration, IApplicationContextService applicationContextService, ISystemExceptionRecordService exceptionRecordService, IDatabaseContext databaseContext, IRedisService redisService)
        {
            this.configuration = configuration;
            this.applicationContextService = applicationContextService;
            this.exceptionRecordService = exceptionRecordService;
            this.databaseContext = databaseContext;
            this.redisService = redisService;
        }

        public void OnActionExecuted(ActionExecutedContext context)
        {
            if (context.Exception != null)
            {
                return;
            }
        }

        public void OnActionExecuting(ActionExecutingContext context)
        {
            this.actionArguments = context.ActionArguments;
            this.applicationNamespace = context.Controller.GetType().FullName.Replace("BCTMSRESTful.Controllers", "").TrimStart('.');

            if (this.applicationNamespace.IndexOf(".") > -1)
            {
                this.applicationNamespace = this.applicationNamespace.Substring(0, this.applicationNamespace.IndexOf("."));
            }
        }

        public override void OnException(ExceptionContext context)
        {
            redisService.Dispose();
            databaseContext.Rollback();

            if (context.Exception is ObjectNotFoundException)
            {
                context.Result = new NoContentResult();
                context.ExceptionHandled = true;

                base.OnException(context);

                return;
            }

            string userInfo = string.Empty;

            switch (this.applicationNamespace)
            {
                case "User":
                    userInfo = JsonConvert.SerializeObject(applicationContextService.User);
                    break;
                default:
                    userInfo = "Anonymous";
                    break;
            }

            string errorUserInfo = string.Empty;
            try
            {
                errorUserInfo = this.ConvertActionArguments(userInfo);
            }
            catch
            {
                errorUserInfo = "异常捕获中存在未解析异常信息。" + userInfo;
            }

            SystemExceptionRecordDto exceptionDto = new SystemExceptionRecordDto
            {
                Name = context.Exception.GetType().Name,
                HelpLink = context.Exception.HelpLink,
                Message = context.Exception.Message,
                StackTrace = context.Exception.StackTrace,
                HResult = context.Exception.HResult,
                Data = errorUserInfo,
                CreateTime = DateTime.Now
            };
            string source = string.Empty;
            exceptionDto.InnerException = context.Exception.InnerException == null ? null : GetInnerExceptionMessage(context.Exception.InnerException, ref source);
            exceptionDto.Source = string.IsNullOrWhiteSpace(source) ? context.Exception.Source : source;

            if (context.Exception is ArgumentException)
            {
                if (Convert.ToBoolean(configuration["EnabledExceptionRecord:IsOpen"]))
                {
                    exceptionRecordService.Create(exceptionDto);
                }
                string errorLabel = context.Exception.Message;

                ErrorMessageDto errorMessage = new ErrorMessageDto
                {
                    ErrorCode = configuration[$"ErrorInfo:{errorLabel}:Code"] ?? "999999998",
                    ErrorMessage = configuration[$"ErrorInfo:{errorLabel}:Message"] ?? errorLabel
                };
                LogHelper.Warn("请求出现警告", context.Exception);
                context.Result = new ObjectResult(errorMessage) { StatusCode = 400 };

            }
            else if (context.Exception is NotImplementedException)
            {
                LogHelper.Error("出现未实现异常", context.Exception);
                context.Result = new StatusCodeResult(501);
            }
            else
            {
                exceptionRecordService.Create(exceptionDto);

                ErrorMessageDto messageDto = new ErrorMessageDto
                {
                    ErrorCode = "999999999"
                };

                if (context.HttpContext.Request.Host.Host == webHostName)
                {
                    messageDto.ErrorMessage = "服务器内部错误。";
                }
                else
                {
                    messageDto.ErrorMessage = context.Exception.Message;
                }
                LogHelper.Warn("出现严重异常", context.Exception);
                context.Result = new ObjectResult(messageDto) { StatusCode = 500 };
            }

            context.ExceptionHandled = true;

            base.OnException(context);
        }

        private string GetInnerExceptionMessage(Exception ex, ref string source)
        {
            StringBuilder message = new StringBuilder();

            if (ex.InnerException != null)
            {
                message.AppendLine(string.Format("The exception source:{0}", ex.Source));
                message.AppendLine(JObject.FromObject(ex.Data).ToString());
                message.AppendLine(ex.Message);
                message.AppendLine(GetInnerExceptionMessage(ex.InnerException, ref source));
            }
            else
            {
                source = ex.Source;

                message.AppendLine(string.Format("The exception source:{0}", ex.Source));
                message.AppendLine(JObject.FromObject(ex.Data).ToString());
                message.AppendLine(ex.Message);
            }

            return message.ToString();
        }

        private string ConvertActionArguments(string currentUserInfo)
        {
            Dictionary<string, string> dictionary = new Dictionary<string, string>();
            if (this.actionArguments != null)
            {

                var obj = new
                {
                    RequestContext = JsonConvert.SerializeObject(this.actionArguments),
                    OperationUser = currentUserInfo
                };
                return JsonConvert.SerializeObject(obj);
            }
            else
            {
                var obj = new
                {
                    RequestContext = JsonConvert.SerializeObject(dictionary),
                    OperationUser = currentUserInfo
                };
                return JsonConvert.SerializeObject(obj);
            }
        }
    }
}